{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1259b655-cb53-4a6e-8899-b3acb4a66560",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "# 激活函数及其导数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "d03e8432-0d0e-4956-8809-7bca0b6de217",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "def sigmoid(x):\n",
    "    return 1 / (1 + np.exp(-x))\n",
    "\n",
    "def sigmoid_derivative(x):\n",
    "    return sigmoid(x) * (1 - sigmoid(x))\n",
    "\n",
    "def relu(x):\n",
    "    return np.maximum(0, x)\n",
    "\n",
    "def relu_derivative(x):\n",
    "    return np.where(x <= 0, 0, 1)\n",
    "\n",
    "# softmax函数用于多类别的输出层\n",
    "def softmax(x):\n",
    "    exps = np.exp(x - np.max(x, axis=1, keepdims=True))\n",
    "    return exps / np.sum(exps, axis=1, keepdims=True)\n",
    "\n",
    "# 交叉熵损失函数\n",
    "def cross_entropy_loss(y_pred, y_true):\n",
    "    m = y_true.shape[0]\n",
    "    loss = -np.sum(y_true * np.log(y_pred + 1e-9)) / m\n",
    "    return loss\n",
    "\n",
    "# 将类别向量转换为二进制（独热编码）类矩阵\n",
    "def to_categorical(y, num_classes):\n",
    "    return np.eye(num_classes)[y.reshape(-1)]\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cb186709-5fc2-4f73-b946-289a8735331a",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "# MLP类定义"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "1ceaf678-3b10-424d-9cd5-242a95c2c9b3",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "class MLP:\n",
    "    def __init__(self, layer_sizes, activation='relu', optimizer=None):\n",
    "        self.layer_sizes = layer_sizes\n",
    "        self.activation = activation\n",
    "        self.optimizer = optimizer\n",
    "        self.weights = [np.random.randn(x, y) * np.sqrt(2. / x) for x, y in zip(layer_sizes[:-1], layer_sizes[1:])]\n",
    "\n",
    "        self.biases = [np.zeros((1, y)) for y in layer_sizes[1:]]\n",
    "        \n",
    "        if activation == 'relu':\n",
    "            self.activation_func = relu\n",
    "            self.activation_derivative = relu_derivative\n",
    "        else:\n",
    "            self.activation_func = sigmoid\n",
    "            self.activation_derivative = sigmoid_derivative\n",
    "\n",
    "    def forward(self, X):\n",
    "        self.A = [X]\n",
    "        for i, (w, b) in enumerate(zip(self.weights, self.biases)):\n",
    "            #print(f\"Layer {i}: input shape: {self.A[-1].shape}, weight shape: {w.shape}\")\n",
    "            z = np.dot(self.A[-1], w) + b\n",
    "            if i < len(self.weights) - 1:\n",
    "                self.A.append(self.activation_func(z))\n",
    "            else:\n",
    "                self.A.append(softmax(z))\n",
    "        return self.A[-1]\n",
    "\n",
    "    def backward(self, X, Y):\n",
    "        m = Y.shape[0]\n",
    "        \n",
    "        self.forward(X)\n",
    "        dA = self.A[-1] - Y\n",
    "        dWs = []\n",
    "        dBs = []\n",
    "        for i in reversed(range(len(self.weights))):\n",
    "            dW = np.dot(self.A[i].T, dA) / m\n",
    "            dB = np.sum(dA, axis=0, keepdims=True) / m\n",
    "            dWs.insert(0, dW)\n",
    "            dBs.insert(0, dB)\n",
    "            if i > 0:\n",
    "                dA = np.dot(dA, self.weights[i].T) * self.activation_derivative(self.A[i])\n",
    "        if self.optimizer:\n",
    "            self.optimizer.update(self.weights + self.biases, dWs + dBs)\n",
    "        else:\n",
    "            for i in range(len(self.weights)):\n",
    "                self.weights[i] -= self.learning_rate * dWs[i]\n",
    "                self.biases[i] -= self.learning_rate * dBs[i]\n",
    "\n",
    "    def train(self, X, Y, epochs):\n",
    "        for epoch in range(epochs):\n",
    "            self.backward(X, Y)\n",
    "            if epoch % 100 == 0:\n",
    "                loss = cross_entropy_loss(self.forward(X), Y)\n",
    "                print(f\"Epoch {epoch}, Loss: {loss}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b15c52e-2867-4771-81ef-00078883ed34",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "# 优化器"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d7dc619e-8d36-4c9b-b3a7-6b9f5cf4cd73",
   "metadata": {},
   "source": [
    "## 优化器基类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "ecbf2330-086d-415c-9eb2-e6d0ce4cda7a",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Optimizer:\n",
    "    def __init__(self, learning_rate=0.01):\n",
    "        self.learning_rate = learning_rate  # 初始化学习率\n",
    "\n",
    "    def update(self, params, grads):\n",
    "        # 更新参数的方法，需要在子类中实现\n",
    "        raise NotImplementedError(\"This method should be overridden by subclasses.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28c83fd7-5d3d-4db9-9cae-8bb79d502a7d",
   "metadata": {},
   "source": [
    "## 带动量的SGD（随机梯度下降）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "cf807216-ae0d-49bd-80e5-bcf0db5eddfa",
   "metadata": {},
   "outputs": [],
   "source": [
    "class SGDMomentum(Optimizer):\n",
    "    def __init__(self, learning_rate=0.01, momentum=0.9):\n",
    "        super().__init__(learning_rate)  # 调用基类的构造函数来设置学习率\n",
    "        self.momentum = momentum  # 动量因子\n",
    "        self.velocity = None  # 初始化速度为None，后续会根据参数形状进行初始化\n",
    "\n",
    "    def update(self, params, grads):\n",
    "        if self.velocity is None:\n",
    "            # 在第一次调用时根据参数的形状初始化速度\n",
    "            self.velocity = [np.zeros_like(p) for p in params]\n",
    "\n",
    "        for v, p, g in zip(self.velocity, params, grads):\n",
    "            v[:] = self.momentum * v - self.learning_rate * g  # 计算速度更新\n",
    "            p += v  # 更新参数\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7baf17c0-996d-44e8-befb-2517f8525db8",
   "metadata": {},
   "source": [
    "## Adagrad（自适应梯度算法）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "3c91f184-8f56-419d-af32-4987191d42a3",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Adagrad(Optimizer):\n",
    "    def __init__(self, learning_rate=0.01, epsilon=1e-8):\n",
    "        super().__init__(learning_rate)  # 初始化学习率\n",
    "        self.epsilon = epsilon  # 避免除以0的小量\n",
    "        self.cache = None  # 用于累积过去梯度的平方\n",
    "\n",
    "    def update(self, params, grads):\n",
    "        if self.cache is None:\n",
    "            # 根据参数形状初始化cache\n",
    "            self.cache = [np.zeros_like(p) for p in params]\n",
    "\n",
    "        for cache, p, g in zip(self.cache, params, grads):\n",
    "            cache[:] += g ** 2  # 累积梯度的平方\n",
    "            p -= self.learning_rate * g / (np.sqrt(cache) + self.epsilon)  # 调整学习步长并更新参数\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2fc00cb1-1582-41f3-b571-67215237dff3",
   "metadata": {},
   "source": [
    "## Adam（自适应矩估计算法）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "b864e01c-2426-48c3-be66-e4f23ee3ee48",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Adam(Optimizer):\n",
    "    def __init__(self, learning_rate=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8):\n",
    "        super().__init__(learning_rate)  # 初始化学习率\n",
    "        self.beta1 = beta1  # 一阶矩估计的指数衰减率\n",
    "        self.beta2 = beta2  # 二阶矩估计的指数衰减率\n",
    "        self.epsilon = epsilon  # 避免除以0\n",
    "        self.m = None  # 一阶矩估计\n",
    "        self.v = None  # 二阶矩估计\n",
    "        self.t = 0  # 初始化时间步\n",
    "\n",
    "    def update(self, params, grads):\n",
    "        if self.m is None:\n",
    "            # 第一次调用时根据参数形状初始化m和v\n",
    "            self.m = [np.zeros_like(p) for p in params]\n",
    "            self.v = [np.zeros_like(p) for p in params]\n",
    "\n",
    "        self.t += 1  # 更新时间步\n",
    "        for m, v, p, g in zip(self.m, self.v, params, grads):\n",
    "            m[:] = self.beta1 * m + (1 - self.beta1) * g  # 更新一阶矩估计\n",
    "            v[:] = self.beta2 * v + (1 - self.beta2) * (g ** 2)  # 更新二阶矩估计\n",
    "            m_hat = m / (1 - self.beta1 ** self.t)  # 计算偏差校正后的一阶矩估计\n",
    "            v_hat = v / (1 - self.beta2 ** self.t)  # 计算偏差校正后的二阶矩估计\n",
    "            p -= self.learning_rate * m_hat / (np.sqrt(v_hat) + self.epsilon)  # 更新参数\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee5ee648-6c52-4fdb-ab64-39521d9a9b29",
   "metadata": {},
   "source": [
    "## 在SGDMomentum中加入Elastic Net正则化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "af921732-0dec-4695-89e6-1af30afde76d",
   "metadata": {},
   "outputs": [],
   "source": [
    "class SGDMomentumElasticNet(SGDMomentum):\n",
    "    def __init__(self, learning_rate=0.01, momentum=0.9, lambda_l1=0.0, lambda_l2=0.0):\n",
    "        super().__init__(learning_rate, momentum)\n",
    "        self.lambda_l1 = lambda_l1  # L1正则化系数\n",
    "        self.lambda_l2 = lambda_l2  # L2正则化系数\n",
    "\n",
    "    def update(self, params, grads):\n",
    "        if self.velocity is None:\n",
    "            self.velocity = [np.zeros_like(p) for p in params]\n",
    "\n",
    "        for i, (v, p, g) in enumerate(zip(self.velocity, params, grads)):\n",
    "            # 加入Elastic Net正则化梯度（L1 + L2）\n",
    "            g += self.lambda_l1 * np.sign(p) + 2 * self.lambda_l2 * p\n",
    "\n",
    "            v[:] = self.momentum * v - self.learning_rate * g\n",
    "            params[i] += v\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db388cf0-032d-47ec-8f9a-c5bbc49f4b0e",
   "metadata": {},
   "source": [
    "## 在Adagrad中加入Elastic Net正则化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "507fdd41-c89a-452a-a9f7-cea2dbc73e0d",
   "metadata": {},
   "outputs": [],
   "source": [
    "class AdagradElasticNet(Adagrad):\n",
    "    def __init__(self, learning_rate=0.01, epsilon=1e-8, lambda_l1=0.0, lambda_l2=0.0):\n",
    "        super().__init__(learning_rate, epsilon)\n",
    "        self.lambda_l1 = lambda_l1  # L1正则化系数\n",
    "        self.lambda_l2 = lambda_l2  # L2正则化系数\n",
    "\n",
    "    def update(self, params, grads):\n",
    "        if self.cache is None:\n",
    "            self.cache = [np.zeros_like(p) for p in params]\n",
    "\n",
    "        for i, (cache, p, g) in enumerate(zip(self.cache, params, grads)):\n",
    "            # 加入Elastic Net正则化梯度（L1 + L2）\n",
    "            g += self.lambda_l1 * np.sign(p) + 2 * self.lambda_l2 * p\n",
    "\n",
    "            cache[:] += g ** 2\n",
    "            params[i] -= self.learning_rate * g / (np.sqrt(cache) + self.epsilon)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8372b146-b160-46f2-acc9-5615671a986b",
   "metadata": {},
   "source": [
    "## 在Adam中加入Elastic Net正则化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "0db194f8-4e54-43e5-9796-8c09ce522997",
   "metadata": {},
   "outputs": [],
   "source": [
    "class AdamElasticNet(Adam):\n",
    "    def __init__(self, learning_rate=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8, lambda_l1=0.0, lambda_l2=0.0):\n",
    "        super().__init__(learning_rate, beta1, beta2, epsilon)\n",
    "        self.lambda_l1 = lambda_l1  # L1正则化系数\n",
    "        self.lambda_l2 = lambda_l2  # L2正则化系数\n",
    "\n",
    "    def update(self, params, grads):\n",
    "        if self.m is None or self.v is None:\n",
    "        # 第一次调用时根据参数形状初始化m和v\n",
    "            self.m = [np.zeros_like(p) for p in params]\n",
    "            self.v = [np.zeros_like(p) for p in params]\n",
    "        \n",
    "        self.t += 1\n",
    "        for i, (m, v, p, g) in enumerate(zip(self.m, self.v, params, grads)):\n",
    "            # 加入Elastic Net正则化梯度（L1 + L2）\n",
    "            g += self.lambda_l1 * np.sign(p) + 2 * self.lambda_l2 * p\n",
    "            m[:] = self.beta1 * m + (1 - self.beta1) * g\n",
    "            v[:] = self.beta2 * v + (1 - self.beta2) * (g ** 2)\n",
    "            m_hat = m / (1 - self.beta1 ** self.t)\n",
    "            v_hat = v / (1 - self.beta2 ** self.t)\n",
    "            p -= self.learning_rate * m_hat / (np.sqrt(v_hat) + self.epsilon)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0e30792-6a1e-4cc2-baf3-c0b44b9a8cc4",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "# 模型评估方法定义"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "a7a72ebd-037f-4b16-9f47-3b2f2213ecb3",
   "metadata": {},
   "outputs": [],
   "source": [
    "def confusion_matrix(y_true, y_pred):\n",
    "    \"\"\"\n",
    "    计算混淆矩阵。\n",
    "    y_true: 真实标签数组。\n",
    "    y_pred: 预测标签数组。\n",
    "    \"\"\"\n",
    "    unique_labels = np.unique(y_true)\n",
    "    matrix = np.zeros((len(unique_labels), len(unique_labels)), dtype=int)\n",
    "    for true_label, pred_label in zip(y_true, y_pred):\n",
    "        matrix[true_label, pred_label] += 1\n",
    "    return matrix\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "c505e566-efc8-47eb-a74e-8ccc12edf0c7",
   "metadata": {},
   "outputs": [],
   "source": [
    "def roc_curve(y_true, y_score):\n",
    "    \"\"\"\n",
    "    计算ROC曲线的FPR和TPR。\n",
    "    y_true: 真实标签数组。\n",
    "    y_score: 预测得分或概率数组。\n",
    "    \"\"\"\n",
    "    thresholds = np.unique(y_score)\n",
    "    tpr = []\n",
    "    fpr = []\n",
    "    \n",
    "    for threshold in thresholds:\n",
    "        y_pred = (y_score >= threshold).astype(int)\n",
    "        FP = np.sum((y_pred == 1) & (y_true == 0))\n",
    "        TP = np.sum((y_pred == 1) & (y_true == 1))\n",
    "        FN = np.sum((y_pred == 0) & (y_true == 1))\n",
    "        TN = np.sum((y_pred == 0) & (y_true == 0))\n",
    "        \n",
    "        tpr.append(TP / (TP + FN))\n",
    "        fpr.append(FP / (FP + TN))\n",
    "    \n",
    "    return np.array(fpr), np.array(tpr), thresholds\n",
    "\n",
    "def auc(fpr, tpr):\n",
    "    \"\"\"\n",
    "    计算AUC值。\n",
    "    fpr: 假正率数组。\n",
    "    tpr: 真正率数组。\n",
    "    \"\"\"\n",
    "    return np.trapz(tpr, fpr)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "d14b574d-9305-428d-99db-f9ec4e0c33bc",
   "metadata": {},
   "outputs": [],
   "source": [
    "def cross_validation_split(dataset, folds=3):\n",
    "    \"\"\"\n",
    "    将数据集分割为训练集和测试集。\n",
    "    dataset: 数据集。\n",
    "    folds: 折数。\n",
    "    \"\"\"\n",
    "    dataset_split = []\n",
    "    dataset_copy = list(dataset)\n",
    "    fold_size = int(len(dataset) / folds)\n",
    "    for _ in range(folds):\n",
    "        fold = []\n",
    "        while len(fold) < fold_size:\n",
    "            index = randrange(len(dataset_copy))\n",
    "            fold.append(dataset_copy.pop(index))\n",
    "        dataset_split.append(fold)\n",
    "    return dataset_split\n",
    "\n",
    "def cross_validate(model, data, labels, folds=3):\n",
    "    \"\"\"\n",
    "    进行交叉验证。\n",
    "    model: 使用的模型。\n",
    "    data: 数据集。\n",
    "    labels: 标签。\n",
    "    folds: 折数。\n",
    "    \"\"\"\n",
    "    results = []\n",
    "    cv_splits = cross_validation_split(list(zip(data, labels)), folds)\n",
    "    \n",
    "    for fold in cv_splits:\n",
    "        train_set = list(cv_splits)\n",
    "        train_set.remove(fold)\n",
    "        train_set = sum(train_set, [])\n",
    "        test_set = list(fold)\n",
    "        \n",
    "        train_data, train_labels = zip(*train_set)\n",
    "        test_data, test_labels = zip(*test_set)\n",
    "        \n",
    "        model.fit(np.array(train_data), np.array(train_labels))\n",
    "        predictions = model.predict(np.array(test_data))\n",
    "        \n",
    "        accuracy = np.mean(predictions == np.array(test_labels))\n",
    "        results.append(accuracy)\n",
    "    \n",
    "    return results\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2bb34099-551d-4b8f-a12d-f572de84aefe",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "# 模型训练"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30963cbf-7e18-42d6-9862-88927dec531c",
   "metadata": {},
   "source": [
    "## 自己写的MLP"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "39c5f848-601e-4430-ad80-be24ffe90f8d",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "train_df = pd.read_csv('C:/Users/96237/Desktop/DLProject/A1 Multi-layer Perceptron Implementation/dataset/mnist_train.csv')\n",
    "test_df = pd.read_csv('C:/Users/96237/Desktop/DLProject/A1 Multi-layer Perceptron Implementation/dataset/mnist_test.csv')\n",
    "\n",
    "# 分离特征和标签\n",
    "X_train = train_df.drop(['label'], axis=1).values / 255.0  # 归一化\n",
    "y_train = train_df['label'].values\n",
    "\n",
    "X_test = test_df.drop(['label'], axis=1).values / 255.0  # 归一化\n",
    "y_test = test_df['label'].values\n",
    "\n",
    "\n",
    "# 将图像数据展平成向量\n",
    "X_train_flat = X_train.reshape(X_train.shape[0], -1)\n",
    "X_test_flat = X_test.reshape(X_test.shape[0], -1)\n",
    "\n",
    "# 将标签转换为独热编码\n",
    "y_train_cat = to_categorical(y_train, 10)\n",
    "y_test_cat = to_categorical(y_test, 10)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "24f2b060-2379-4376-b199-7139d7ef289c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 0, Loss: 1.9972903736117933\n",
      "Epoch 100, Loss: 0.14350631697916844\n",
      "Epoch 200, Loss: 0.0985962663567354\n",
      "Epoch 300, Loss: 0.07537894968886533\n",
      "Epoch 400, Loss: 0.06170981389606658\n"
     ]
    }
   ],
   "source": [
    "# 定义模型和优化器\n",
    "optimizer = AdamElasticNet(learning_rate=0.001, lambda_l1=0.0001, lambda_l2=0.0001)\n",
    "model_selfwrite = MLP(layer_sizes=[784, 512, 256, 10], activation='relu', optimizer=optimizer)\n",
    "\n",
    "\n",
    "# 训练模型\n",
    "epochs = 500  \n",
    "# 训练模型时使用独热编码的标签\n",
    "model_selfwrite.train(X_train_flat, y_train_cat, epochs=epochs)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15554d88-5c8a-41cf-bf78-6fbe94dc762b",
   "metadata": {},
   "source": [
    "## TensorFlow"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "f567563f-1d88-427b-838e-155dc7690983",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Software\\Python\\Lib\\site-packages\\keras\\src\\layers\\core\\dense.py:86: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(activity_regularizer=activity_regularizer, **kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/10\n",
      "\u001b[1m469/469\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m2s\u001b[0m 4ms/step - accuracy: 0.8813 - loss: 0.4090\n",
      "Epoch 2/10\n",
      "\u001b[1m469/469\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m2s\u001b[0m 4ms/step - accuracy: 0.9747 - loss: 0.0848\n",
      "Epoch 3/10\n",
      "\u001b[1m469/469\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m2s\u001b[0m 4ms/step - accuracy: 0.9850 - loss: 0.0511\n",
      "Epoch 4/10\n",
      "\u001b[1m469/469\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m2s\u001b[0m 4ms/step - accuracy: 0.9895 - loss: 0.0342\n",
      "Epoch 5/10\n",
      "\u001b[1m469/469\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m2s\u001b[0m 4ms/step - accuracy: 0.9909 - loss: 0.0279\n",
      "Epoch 6/10\n",
      "\u001b[1m469/469\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m2s\u001b[0m 4ms/step - accuracy: 0.9934 - loss: 0.0218\n",
      "Epoch 7/10\n",
      "\u001b[1m469/469\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m2s\u001b[0m 4ms/step - accuracy: 0.9951 - loss: 0.0155\n",
      "Epoch 8/10\n",
      "\u001b[1m469/469\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m2s\u001b[0m 4ms/step - accuracy: 0.9949 - loss: 0.0145\n",
      "Epoch 9/10\n",
      "\u001b[1m469/469\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m2s\u001b[0m 4ms/step - accuracy: 0.9960 - loss: 0.0113\n",
      "Epoch 10/10\n",
      "\u001b[1m469/469\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m2s\u001b[0m 4ms/step - accuracy: 0.9965 - loss: 0.0104\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<keras.src.callbacks.history.History at 0x21deac70e90>"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import tensorflow as tf\n",
    "from tensorflow.keras.layers import Dense, Flatten\n",
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.datasets import mnist\n",
    "from tensorflow.keras.utils import to_categorical\n",
    "\n",
    "# 加载MNIST数据集\n",
    "(train_images, train_labels), (test_images, test_labels) = mnist.load_data()\n",
    "\n",
    "# 数据预处理\n",
    "train_images = train_images.reshape((60000, 28 * 28)).astype('float32') / 255\n",
    "test_images = test_images.reshape((10000, 28 * 28)).astype('float32') / 255\n",
    "\n",
    "train_labels = to_categorical(train_labels)\n",
    "test_labels = to_categorical(test_labels)\n",
    "\n",
    "# 构建模型\n",
    "model_tf = Sequential([\n",
    "    Dense(512, activation='relu', input_shape=(28 * 28,)),\n",
    "    Dense(256, activation='relu'),\n",
    "    Dense(10, activation='softmax')\n",
    "])\n",
    "\n",
    "# 编译模型\n",
    "model_tf.compile(optimizer='adam',\n",
    "              loss='categorical_crossentropy',\n",
    "              metrics=['accuracy'])\n",
    "\n",
    "# 训练模型\n",
    "model_tf.fit(train_images, train_labels, epochs=10, batch_size=128)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9105707f-03c8-404f-80b1-a7e892f58e12",
   "metadata": {},
   "source": [
    "## pytorch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "11148df9-2eea-4914-82da-ebd37e8370ed",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train Epoch: 1 [0/60000 (0%)]\tLoss: 2.309881\n",
      "Train Epoch: 1 [6400/60000 (11%)]\tLoss: 0.546608\n",
      "Train Epoch: 1 [12800/60000 (21%)]\tLoss: 0.441711\n",
      "Train Epoch: 1 [19200/60000 (32%)]\tLoss: 0.242418\n",
      "Train Epoch: 1 [25600/60000 (43%)]\tLoss: 0.267168\n",
      "Train Epoch: 1 [32000/60000 (53%)]\tLoss: 0.176076\n",
      "Train Epoch: 1 [38400/60000 (64%)]\tLoss: 0.185431\n",
      "Train Epoch: 1 [44800/60000 (75%)]\tLoss: 0.116122\n",
      "Train Epoch: 1 [51200/60000 (85%)]\tLoss: 0.414214\n",
      "Train Epoch: 1 [57600/60000 (96%)]\tLoss: 0.097113\n",
      "\n",
      "Test set: Average loss: 0.0002, Accuracy: 9444/10000 (94%)\n",
      "Train Epoch: 2 [0/60000 (0%)]\tLoss: 0.182682\n",
      "Train Epoch: 2 [6400/60000 (11%)]\tLoss: 0.267230\n",
      "Train Epoch: 2 [12800/60000 (21%)]\tLoss: 0.099007\n",
      "Train Epoch: 2 [19200/60000 (32%)]\tLoss: 0.375220\n",
      "Train Epoch: 2 [25600/60000 (43%)]\tLoss: 0.108556\n",
      "Train Epoch: 2 [32000/60000 (53%)]\tLoss: 0.260322\n",
      "Train Epoch: 2 [38400/60000 (64%)]\tLoss: 0.115382\n",
      "Train Epoch: 2 [44800/60000 (75%)]\tLoss: 0.243149\n",
      "Train Epoch: 2 [51200/60000 (85%)]\tLoss: 0.052177\n",
      "Train Epoch: 2 [57600/60000 (96%)]\tLoss: 0.089264\n",
      "\n",
      "Test set: Average loss: 0.0001, Accuracy: 9581/10000 (96%)\n",
      "Train Epoch: 3 [0/60000 (0%)]\tLoss: 0.117767\n",
      "Train Epoch: 3 [6400/60000 (11%)]\tLoss: 0.166498\n",
      "Train Epoch: 3 [12800/60000 (21%)]\tLoss: 0.046411\n",
      "Train Epoch: 3 [19200/60000 (32%)]\tLoss: 0.147532\n",
      "Train Epoch: 3 [25600/60000 (43%)]\tLoss: 0.048311\n",
      "Train Epoch: 3 [32000/60000 (53%)]\tLoss: 0.110803\n",
      "Train Epoch: 3 [38400/60000 (64%)]\tLoss: 0.098753\n",
      "Train Epoch: 3 [44800/60000 (75%)]\tLoss: 0.067399\n",
      "Train Epoch: 3 [51200/60000 (85%)]\tLoss: 0.165462\n",
      "Train Epoch: 3 [57600/60000 (96%)]\tLoss: 0.043865\n",
      "\n",
      "Test set: Average loss: 0.0001, Accuracy: 9683/10000 (97%)\n",
      "Train Epoch: 4 [0/60000 (0%)]\tLoss: 0.248663\n",
      "Train Epoch: 4 [6400/60000 (11%)]\tLoss: 0.045180\n",
      "Train Epoch: 4 [12800/60000 (21%)]\tLoss: 0.159959\n",
      "Train Epoch: 4 [19200/60000 (32%)]\tLoss: 0.147586\n",
      "Train Epoch: 4 [25600/60000 (43%)]\tLoss: 0.059096\n",
      "Train Epoch: 4 [32000/60000 (53%)]\tLoss: 0.033802\n",
      "Train Epoch: 4 [38400/60000 (64%)]\tLoss: 0.074151\n",
      "Train Epoch: 4 [44800/60000 (75%)]\tLoss: 0.093710\n",
      "Train Epoch: 4 [51200/60000 (85%)]\tLoss: 0.019166\n",
      "Train Epoch: 4 [57600/60000 (96%)]\tLoss: 0.196814\n",
      "\n",
      "Test set: Average loss: 0.0001, Accuracy: 9705/10000 (97%)\n",
      "Train Epoch: 5 [0/60000 (0%)]\tLoss: 0.034291\n",
      "Train Epoch: 5 [6400/60000 (11%)]\tLoss: 0.051342\n",
      "Train Epoch: 5 [12800/60000 (21%)]\tLoss: 0.014332\n",
      "Train Epoch: 5 [19200/60000 (32%)]\tLoss: 0.036407\n",
      "Train Epoch: 5 [25600/60000 (43%)]\tLoss: 0.033601\n",
      "Train Epoch: 5 [32000/60000 (53%)]\tLoss: 0.090796\n",
      "Train Epoch: 5 [38400/60000 (64%)]\tLoss: 0.015647\n",
      "Train Epoch: 5 [44800/60000 (75%)]\tLoss: 0.140422\n",
      "Train Epoch: 5 [51200/60000 (85%)]\tLoss: 0.079304\n",
      "Train Epoch: 5 [57600/60000 (96%)]\tLoss: 0.115531\n",
      "\n",
      "Test set: Average loss: 0.0001, Accuracy: 9707/10000 (97%)\n",
      "Train Epoch: 6 [0/60000 (0%)]\tLoss: 0.044394\n",
      "Train Epoch: 6 [6400/60000 (11%)]\tLoss: 0.008069\n",
      "Train Epoch: 6 [12800/60000 (21%)]\tLoss: 0.016230\n",
      "Train Epoch: 6 [19200/60000 (32%)]\tLoss: 0.072054\n",
      "Train Epoch: 6 [25600/60000 (43%)]\tLoss: 0.027558\n",
      "Train Epoch: 6 [32000/60000 (53%)]\tLoss: 0.036761\n",
      "Train Epoch: 6 [38400/60000 (64%)]\tLoss: 0.182763\n",
      "Train Epoch: 6 [44800/60000 (75%)]\tLoss: 0.044282\n",
      "Train Epoch: 6 [51200/60000 (85%)]\tLoss: 0.034158\n",
      "Train Epoch: 6 [57600/60000 (96%)]\tLoss: 0.060669\n",
      "\n",
      "Test set: Average loss: 0.0001, Accuracy: 9721/10000 (97%)\n",
      "Train Epoch: 7 [0/60000 (0%)]\tLoss: 0.055253\n",
      "Train Epoch: 7 [6400/60000 (11%)]\tLoss: 0.047270\n",
      "Train Epoch: 7 [12800/60000 (21%)]\tLoss: 0.064056\n",
      "Train Epoch: 7 [19200/60000 (32%)]\tLoss: 0.014281\n",
      "Train Epoch: 7 [25600/60000 (43%)]\tLoss: 0.068866\n",
      "Train Epoch: 7 [32000/60000 (53%)]\tLoss: 0.093986\n",
      "Train Epoch: 7 [38400/60000 (64%)]\tLoss: 0.023713\n",
      "Train Epoch: 7 [44800/60000 (75%)]\tLoss: 0.171481\n",
      "Train Epoch: 7 [51200/60000 (85%)]\tLoss: 0.032415\n",
      "Train Epoch: 7 [57600/60000 (96%)]\tLoss: 0.068887\n",
      "\n",
      "Test set: Average loss: 0.0001, Accuracy: 9748/10000 (97%)\n",
      "Train Epoch: 8 [0/60000 (0%)]\tLoss: 0.053374\n",
      "Train Epoch: 8 [6400/60000 (11%)]\tLoss: 0.044514\n",
      "Train Epoch: 8 [12800/60000 (21%)]\tLoss: 0.083937\n",
      "Train Epoch: 8 [19200/60000 (32%)]\tLoss: 0.018823\n",
      "Train Epoch: 8 [25600/60000 (43%)]\tLoss: 0.062424\n",
      "Train Epoch: 8 [32000/60000 (53%)]\tLoss: 0.090523\n",
      "Train Epoch: 8 [38400/60000 (64%)]\tLoss: 0.005266\n",
      "Train Epoch: 8 [44800/60000 (75%)]\tLoss: 0.071854\n",
      "Train Epoch: 8 [51200/60000 (85%)]\tLoss: 0.006973\n",
      "Train Epoch: 8 [57600/60000 (96%)]\tLoss: 0.011483\n",
      "\n",
      "Test set: Average loss: 0.0001, Accuracy: 9743/10000 (97%)\n",
      "Train Epoch: 9 [0/60000 (0%)]\tLoss: 0.033168\n",
      "Train Epoch: 9 [6400/60000 (11%)]\tLoss: 0.013726\n",
      "Train Epoch: 9 [12800/60000 (21%)]\tLoss: 0.005648\n",
      "Train Epoch: 9 [19200/60000 (32%)]\tLoss: 0.046924\n",
      "Train Epoch: 9 [25600/60000 (43%)]\tLoss: 0.075697\n",
      "Train Epoch: 9 [32000/60000 (53%)]\tLoss: 0.040576\n",
      "Train Epoch: 9 [38400/60000 (64%)]\tLoss: 0.006042\n",
      "Train Epoch: 9 [44800/60000 (75%)]\tLoss: 0.040003\n",
      "Train Epoch: 9 [51200/60000 (85%)]\tLoss: 0.078448\n",
      "Train Epoch: 9 [57600/60000 (96%)]\tLoss: 0.006692\n",
      "\n",
      "Test set: Average loss: 0.0001, Accuracy: 9770/10000 (98%)\n",
      "Train Epoch: 10 [0/60000 (0%)]\tLoss: 0.021837\n",
      "Train Epoch: 10 [6400/60000 (11%)]\tLoss: 0.005546\n",
      "Train Epoch: 10 [12800/60000 (21%)]\tLoss: 0.000762\n",
      "Train Epoch: 10 [19200/60000 (32%)]\tLoss: 0.006973\n",
      "Train Epoch: 10 [25600/60000 (43%)]\tLoss: 0.058522\n",
      "Train Epoch: 10 [32000/60000 (53%)]\tLoss: 0.036638\n",
      "Train Epoch: 10 [38400/60000 (64%)]\tLoss: 0.003473\n",
      "Train Epoch: 10 [44800/60000 (75%)]\tLoss: 0.021528\n",
      "Train Epoch: 10 [51200/60000 (85%)]\tLoss: 0.056548\n",
      "Train Epoch: 10 [57600/60000 (96%)]\tLoss: 0.048023\n",
      "\n",
      "Test set: Average loss: 0.0001, Accuracy: 9736/10000 (97%)\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "import torch.optim as optim\n",
    "from torchvision import datasets, transforms\n",
    "from torch.utils.data import DataLoader\n",
    "\n",
    "# 检查是否可以使用GPU\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
    "\n",
    "# 数据预处理：转换PIL图像为Tensor并标准化\n",
    "transform = transforms.Compose([\n",
    "    transforms.ToTensor(),\n",
    "    transforms.Normalize((0.5,), (0.5,))\n",
    "])\n",
    "\n",
    "# 加载训练集和测试集\n",
    "train_dataset = datasets.MNIST('./data', train=True, download=True, transform=transform)\n",
    "test_dataset = datasets.MNIST('./data', train=False, transform=transform)\n",
    "\n",
    "train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)\n",
    "test_loader = DataLoader(test_dataset, batch_size=1000, shuffle=False)\n",
    "\n",
    "# 定义多层感知机模型\n",
    "class MLP_PT(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(MLP_PT, self).__init__()\n",
    "        self.fc1 = nn.Linear(28*28, 512)\n",
    "        self.fc2 = nn.Linear(512, 256)\n",
    "        self.fc3 = nn.Linear(256, 10)\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = x.view(-1, 28*28)\n",
    "        x = F.relu(self.fc1(x))\n",
    "        x = F.relu(self.fc2(x))\n",
    "        x = self.fc3(x)\n",
    "        return F.log_softmax(x, dim=1)\n",
    "\n",
    "model_pt = MLP_PT().to(device)\n",
    "\n",
    "# 定义损失函数和优化器\n",
    "criterion = nn.CrossEntropyLoss()\n",
    "optimizer = optim.Adam(model_pt.parameters(), lr=0.001)\n",
    "\n",
    "# 训练模型\n",
    "def train(model_pt, device, train_loader, optimizer, epoch):\n",
    "    model_pt.train()\n",
    "    for batch_idx, (data, target) in enumerate(train_loader):\n",
    "        data, target = data.to(device), target.to(device)\n",
    "        optimizer.zero_grad()\n",
    "        output = model_pt(data)\n",
    "        loss = criterion(output, target)\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        if batch_idx % 100 == 0:\n",
    "            print(f'Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)}'\n",
    "                  f' ({100. * batch_idx / len(train_loader):.0f}%)]\\tLoss: {loss.item():.6f}')\n",
    "\n",
    "# 测试模型\n",
    "def test(model_pt, device, test_loader):\n",
    "    model_pt.eval()\n",
    "    test_loss = 0\n",
    "    correct = 0\n",
    "    with torch.no_grad():\n",
    "        for data, target in test_loader:\n",
    "            data, target = data.to(device), target.to(device)\n",
    "            output = model_pt(data)\n",
    "            test_loss += criterion(output, target).item()\n",
    "            pred = output.argmax(dim=1, keepdim=True)\n",
    "            correct += pred.eq(target.view_as(pred)).sum().item()\n",
    "    test_loss /= len(test_loader.dataset)\n",
    "    print(f'\\nTest set: Average loss: {test_loss:.4f}, Accuracy: {correct}/{len(test_loader.dataset)}'\n",
    "          f' ({100. * correct / len(test_loader.dataset):.0f}%)')\n",
    "\n",
    "# 执行训练和测试\n",
    "for epoch in range(1, 11):\n",
    "    train(model_pt, device, train_loader, optimizer, epoch)\n",
    "    test(model_pt, device, test_loader)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "39a0b90c-7fb0-4ef2-bd8c-83d9549993c5",
   "metadata": {},
   "source": [
    "# 模型评估"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e613cf12-ce21-4b11-8c8d-3499af0b0d2e",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "## 混淆矩阵"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "d19e1269-1691-491f-bfee-df93d2b53aad",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SelfWrite MLP Confusion Matrix:\n",
      " [[ 969    2    0    1    0    2    3    0    2    1]\n",
      " [   0 1121    3    2    0    0    3    0    6    0]\n",
      " [   4    2 1006    3    3    0    2    8    4    0]\n",
      " [   0    0    4  984    0    8    0    6    5    3]\n",
      " [   1    0    4    0  966    0    2    2    0    7]\n",
      " [   3    0    0    7    0  870    4    1    6    1]\n",
      " [   2    4    0    1    6    3  937    0    5    0]\n",
      " [   2    4    9    2    0    0    0 1004    3    4]\n",
      " [   6    0    1    2    2    1    3    3  953    3]\n",
      " [   4    2    0    6    6    2    0    5    2  982]]\n"
     ]
    }
   ],
   "source": [
    "def evaluate_model_cm(X_test, y_test, model):\n",
    "    # 进行预测\n",
    "    predictions = model_selfwrite.forward(X_test)\n",
    "    predicted_labels = np.argmax(predictions, axis=1)\n",
    "\n",
    "    # 计算混淆矩阵\n",
    "    cm = confusion_matrix(y_test, predicted_labels)\n",
    "    return cm\n",
    "\n",
    "# 调用函数\n",
    "cm = evaluate_model_cm(X_test_flat, y_test, model_selfwrite)\n",
    "print(\"SelfWrite MLP Confusion Matrix:\\n\", cm)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "389770f7-2501-4ca1-a4d9-460e0ed8f86e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m313/313\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 1ms/step\n",
      "TensorFlow Confusion Matrix:\n",
      " [[ 974    0    0    1    0    1    1    0    1    2]\n",
      " [   1 1127    2    1    0    0    1    2    1    0]\n",
      " [   2    0 1014    3    1    0    0    4    7    1]\n",
      " [   0    0    4  991    0    8    0    3    4    0]\n",
      " [   0    1    5    0  967    0    2    2    0    5]\n",
      " [   3    1    0    6    1  877    2    1    1    0]\n",
      " [   4    2    0    1    9   11  930    0    1    0]\n",
      " [   1    2    7    0    0    1    0 1010    3    4]\n",
      " [   8    1    3    2    1    9    0    4  944    2]\n",
      " [   1    2    0    3    6   15    0    7    0  975]]\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import confusion_matrix\n",
    "import numpy as np\n",
    "\n",
    "# 预测测试集\n",
    "test_predictions = model_tf.predict(test_images)\n",
    "\n",
    "# 将预测转换为类别标签\n",
    "test_predictions_labels = np.argmax(test_predictions, axis=1)\n",
    "test_true_labels = np.argmax(test_labels, axis=1)\n",
    "\n",
    "# 计算混淆矩阵\n",
    "conf_matrix_tf = confusion_matrix(test_true_labels, test_predictions_labels)\n",
    "print(\"TensorFlow Confusion Matrix:\\n\", conf_matrix_tf)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "f86fddc6-3410-42ae-ac41-4c87b7ebd1b9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "PyTorch Confusion Matrix:\n",
      " [[ 956    0    3    0    1    1   11    5    2    1]\n",
      " [   0 1124    3    1    0    0    1    1    3    2]\n",
      " [   2    0  996    5    1    0    4   14    9    1]\n",
      " [   0    0    5  975    0    2    0   10    9    9]\n",
      " [   0    0    3    0  955    0    6    3    0   15]\n",
      " [   0    0    0   15    2  856    5    1    3   10]\n",
      " [   2    2    0    1    2    9  939    0    3    0]\n",
      " [   0    4    6    1    1    0    0 1005    6    5]\n",
      " [   0    0    3    7    3    1    1    3  949    7]\n",
      " [   1    1    0    2   12    1    1    6    4  981]]\n"
     ]
    }
   ],
   "source": [
    "# 初始化用于混淆矩阵计算的变量\n",
    "all_preds = torch.tensor([]).to(device)\n",
    "all_targets = torch.tensor([]).to(device)\n",
    "\n",
    "# 不更新模型，只进行预测\n",
    "model_pt.eval()\n",
    "with torch.no_grad():\n",
    "    for data, target in test_loader:\n",
    "        data, target = data.to(device), target.to(device)\n",
    "        output = model_pt(data)\n",
    "        all_preds = torch.cat((all_preds, output),dim=0)\n",
    "        all_targets = torch.cat((all_targets, target),dim=0)\n",
    "\n",
    "# 将预测和目标从one-hot编码转换为类别索引\n",
    "_, predicted_labels = torch.max(all_preds, 1)\n",
    "conf_matrix_pt = confusion_matrix(all_targets.cpu(), predicted_labels.cpu())\n",
    "print(\"PyTorch Confusion Matrix:\\n\", conf_matrix_pt)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42adf342-5186-470c-a5c1-f789e2f30865",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "## Accuracy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "ad2c6598-9d20-41e4-bd38-3ce633208049",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " SelfWriteMLP Test Accuracy: 97.92%\n"
     ]
    }
   ],
   "source": [
    "def evaluate_accuracy(X_test, y_test, model_selfwrite):\n",
    "    predictions = model_selfwrite.forward(X_test)\n",
    "    predicted_labels = np.argmax(predictions, axis=1)\n",
    "    accuracy = np.mean(predicted_labels == y_test)\n",
    "    return accuracy\n",
    "\n",
    "accuracy = evaluate_accuracy(X_test_flat, y_test, model_selfwrite)\n",
    "print(f\" SelfWriteMLP Test Accuracy: {accuracy * 100:.2f}%\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "237ce5ce-fe67-4475-928a-a3e92fb3b194",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m313/313\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 1ms/step - accuracy: 0.9775 - loss: 0.0898\n",
      "TensorFlow Test accuracy: 0.9809\n"
     ]
    }
   ],
   "source": [
    "# 评估模型\n",
    "test_loss, test_acc = model_tf.evaluate(test_images, test_labels)\n",
    "print(f'TensorFlow Test accuracy: {test_acc:.4f}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "81b2251d-1fd4-4fcc-bd24-b98806dc072f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "PyTorch Test Accuracy: 97.36%\n"
     ]
    }
   ],
   "source": [
    "def calculate_accuracy(model, device, test_loader):\n",
    "    model.eval()  # 将模型设置为评估模式\n",
    "    correct = 0\n",
    "    total = 0\n",
    "\n",
    "    with torch.no_grad():  # 在评估过程中不计算梯度\n",
    "        for data, target in test_loader:\n",
    "            data, target = data.to(device), target.to(device)\n",
    "            outputs = model(data)\n",
    "            _, predicted = torch.max(outputs.data, 1)\n",
    "            total += target.size(0)\n",
    "            correct += (predicted == target).sum().item()\n",
    "\n",
    "    accuracy = 100 * correct / total\n",
    "    return accuracy\n",
    "\n",
    "accuracy = calculate_accuracy(model_pt, device, test_loader)\n",
    "print(f'PyTorch Test Accuracy: {accuracy:.2f}%')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a512b66d-fb19-44e3-bec6-36d8e30d7610",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "## ROC曲线"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "5598171d-0fd3-48c8-a90e-fb2e97d53699",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "y_prob = model_selfwrite.forward(X_test_flat)  # 确保这返回一个概率分布\n",
    "\n",
    "# 选择一个类别作为正类，这里以类别0为例\n",
    "y_test_binary = (y_test == 0).astype(int)  # 将标签转换为二进制形式\n",
    "y_score = y_prob[:, 0]  \n",
    "\n",
    "from sklearn.metrics import roc_curve, auc\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# 计算ROC曲线\n",
    "fpr, tpr, thresholds = roc_curve(y_test_binary, y_score)\n",
    "\n",
    "# 计算AUC\n",
    "roc_auc = auc(fpr, tpr)\n",
    "\n",
    "# 绘制ROC曲线\n",
    "plt.figure()\n",
    "plt.plot(fpr, tpr, color='darkorange', lw=2, label='ROC curve (area = %0.2f)' % roc_auc)\n",
    "plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')\n",
    "plt.xlim([0.0, 1.0])\n",
    "plt.ylim([0.0, 1.05])\n",
    "plt.xlabel('False Positive Rate')\n",
    "plt.ylabel('True Positive Rate')\n",
    "plt.title('SelfWrite MLP')\n",
    "plt.legend(loc=\"lower right\")\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "a745c15a-3a3d-4bab-aaa0-1f2d9bcfe4fc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m313/313\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 923us/step\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.metrics import roc_curve, auc\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "# 获取模型的预测概率\n",
    "y_prob = model_tf.predict(X_test)  \n",
    "\n",
    "y_true = np.where(y_test == 0, 1, 0)  \n",
    "y_score = y_prob[:, 0]  # 获取正类的预测概率\n",
    "\n",
    "# 计算ROC曲线\n",
    "fpr, tpr, _ = roc_curve(y_true, y_score)\n",
    "roc_auc = auc(fpr, tpr)\n",
    "\n",
    "# 绘制ROC曲线\n",
    "plt.figure()\n",
    "plt.plot(fpr, tpr, color='darkorange', lw=2, label='ROC curve (area = %0.2f)' % roc_auc)\n",
    "plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')\n",
    "plt.xlabel('False Positive Rate')\n",
    "plt.ylabel('True Positive Rate')\n",
    "plt.title('TensorFLow ROC Curve')\n",
    "plt.legend(loc=\"lower right\")\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "e764ead1-24a7-4478-a2a1-7a743cede90b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "model_pt.eval()  # 设置为评估模式\n",
    "y_true = []  # 存储真实标签\n",
    "y_scores = []  # 存储预测概率\n",
    "\n",
    "with torch.no_grad():\n",
    "    for data, target in test_loader:\n",
    "        data, target = data.to(device), target.to(device)\n",
    "        output = model_pt(data)\n",
    "        y_scores.extend(output[:, 0].cpu().numpy())  # 获取正类的预测概率\n",
    "        y_true.extend(target.cpu().numpy())\n",
    "\n",
    "y_true = np.where(np.array(y_true) == 0, 1, 0)\n",
    "y_scores = np.array(y_scores)\n",
    "\n",
    "# 计算ROC曲线\n",
    "fpr, tpr, _ = roc_curve(y_true, y_scores)\n",
    "roc_auc = auc(fpr, tpr)\n",
    "\n",
    "# 绘制ROC曲线\n",
    "plt.figure()\n",
    "plt.plot(fpr, tpr, color='darkorange', lw=2, label='ROC curve (area = %0.2f)' % roc_auc)\n",
    "plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')\n",
    "plt.xlabel('False Positive Rate')\n",
    "plt.ylabel('True Positive Rate')\n",
    "plt.title('PyTorch ROC Curve')\n",
    "plt.legend(loc=\"lower right\")\n",
    "plt.show()\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
